﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace TestLibrary
{
    /// <summary>
    /// This is XML code documentation. It is implemented by entering /// above classes, properties, and method bodies.
    /// While you can ask Visual Studio to create the an XML file on build, we don't need it to in this project. Just remember
    /// that this is how Visual Studio pulls up Intellisense! So if I use your class, I can see descriptions for it
    /// without having to read the code.
    /// </summary>
    public class TestDll
    {
        //In C#, there are Auto Properties. If you know Java, it is like have a private field and then
        //implementing a getter and setter method, but in C#, we are allowed this short hand notation.
        public int Height { get; set; }
        /// <summary>
        /// Sample XML/Intellisense documentation for a property. This is the name of a person, it can be set in the
        /// class constructor or by the public access setter.
        /// </summary>
        public String Name { get; set; }

        //Variations on create class properties:
        
        //Auto property with different visibility levels for the getter or setter
        //note that the specified visibility must be more strict than the property visibility
        public String PrivateSetter { get; private set; }

        //Property with implemented getter and setter.
        //Note this is more like Java, where you create a private field, then make methods, but it
        //still has that C# flare.
        //Naming these properties is styled that the private field is the same as the property, but starts
        //with an underscore and lowercase first letter and the property has no underscore and starts with
        //an uppercase letter.
        private String _implemented;
        public String Implemented
        {
            get
            {
                return _implemented;
            }
            set
            {
                //value is a parameter to the set method, for efficiency, we don't need to reassign the
                //private field if it is already equal to value.
                if (_implemented != value)
                {
                    _implemented = value;
                }
            }
        }

        //Property whose set or get method doesn't play with a private field, but rather computes a return value.
        public int CalcSquareHeight
        {
            //We see two things here. One, we can ignore implementing set. Two, we're using another property of
            //this class to compute an answer. This is just like any other method, but looks like a property!
            //Use case? When you need to interact with private class variables or have no need for parameters to
            //come up with a value.
            get
            {
                int square = Height * Height;
                return square;
            }
        }

        /// <summary>
        /// This is sample method XML/Intellisense documentation. Notice there are param fields now.
        /// TestDll constructor that sets Height and Name properties.
        /// </summary>
        /// <param name="h">Shows up as you get to parameters in making the constructor call,
        /// h sets the Height property.</param>
        /// <param name="n">n sets the Name property.</param>
        public TestDll(int h, String n)
        {
            Height = h;
            Name = n;
        }

        /// <summary>
        /// Default constructor, does nothing, but allows you to use the Object Initializer.
        /// </summary>
        public TestDll()
        { }

        public String SendMessage()
        {
            //In C#, the ?? operator is like a short-hand notation for if(object != null) use this value else use this value.
            //So in this case, use Height and Name properties if they are not null, otherwise Height is displayed as 0 and
            //Name is displayed as Not set. Note this doesn't set the properties to these values, it just helps you output.
            String rv = "Your height is set as " + Height ?? 0 + " and your name is set as " + Name ?? "Not set" + ". Thank you.";

            return rv;
        }
    }

    class TestItOut
    {
        /// <summary>
        /// Sample XML/Intellisense documentation that demonstrates the returns field.
        /// </summary>
        /// <returns>A new TestDll object with Height and Name set.</returns>
        public TestDll ObjectInitExample()
        {
            //Here's a neat example. Need an object with certain properties set, but there isn't a handy
            //constructor to do exactly what you need in one quick line? Use the Object Initializer!
            //To do so, use {} instead of (). This means you can only use the default, no parameter constructor.
            //Name the property you want to set, use the = operator, and properties are comma separated.
            TestDll aTest = new TestDll
            {
                Name = "Adam",
                Height = 77
            };

            //Here's another example while we're at it. The var keyword.
            //In C#, var doesn't mean the object can take on any type like in scripting languages, but it means
            //the compiler will figure out what type the object is supposed to be and type it for you.
            //This is handy if you don't quite know what type you will get back, or if the type is a lot to key in.
            var aTest2 = new TestDll
            {
                Name = "Ghana"
            };

            //A better example of var being short hand notation, we create a complex object.
            //When in doubt as to what type var is, mouse over it and Intellisense will tell you.
            var complex = new List<Tuple<int, String, double>>();
            //By the way, I find List<T> to be a better array, but they are very similar.

            //Two ways to type cast.
            //First is using the 'as' keyword. What it does is do a cast but if the cast fails, you have a null reference.
            Object reallyATestDll = new TestDll();
            //If you are CERTAIN you will not get a null pointer, just use the as keyword.
            TestDll theDll = reallyATestDll as TestDll;
            //This may work when in Asp.net you use Page.FindControl("theName") as TextBox for example, but good practice
            //includes checking for that null.
            TestDll nullCheck;
            if ((nullCheck = reallyATestDll as TestDll) != null)
            {
                nullCheck.Name = "As cast!";
            }

            //The other way to cast, which you guys should be familiar with, is boxing.
            TestDll boxed = (TestDll)reallyATestDll;
            //but this can cause an IllegalCastException, so the 'is' keyword comes into play.
            TestDll isBoxed;
            if (reallyATestDll is TestDll)
            {
                isBoxed = (TestDll)reallyATestDll;
                isBoxed.Name = "Box cast with is!";
            }
            //At the end of the day, using is or as is more or less the same work as far as code goes, but I believe as is more efficient.

            //You cannot as cast all types.
            Object reallyAnInt = 16;
            //int failAsInt = reallyAnInt as int; //uncomment line to see error; basically it's because int is a non-nullable type
            //the solution, if you really want it that badly, is to use a nullable int. Here's how:
            int? nullableInt = reallyAnInt as int?; //use the ? operator appended to the end of the type to make it nullable.
            //remember that the as keyword will give you a null pointer if the cast fails, thus the need for a nullable int.
            int innerValue = nullableInt.Value; //get's you back to a regular int.
            //boxing
            int boxInt;
            boxInt = (int)reallyAnInt;
            //or use is
            if (reallyAnInt is int)
                boxInt = (int)reallyAnInt;

            //int, double, char, bool, string, and probably more are aliases.
            int alias = new Int32();
            long longAlias = new Int64();
            string strAlias = new String(new char[] { 'a', 'b' });
            bool isItTrue = new Boolean();
            //however, we don't need the new keyword here in any of these cases
            Int32 anInt = 12;
            Boolean noItIsFalse = false;
            String str = "Adam";

            //speaking of casting, you may need a string to number
            //there are two ways
            int parsed;
            String numberAsString = "12";
            try
            {
                parsed = Int32.Parse(numberAsString);
                //or
                parsed = Convert.ToInt32(numberAsString); //Convert class can parse more than just string.
            }
            catch (ArgumentException ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message + Environment.NewLine + "That String can't be parsed.");
            }
            //but why try catch, that's a lot of code.
            //still using variables parsed and numberAsString
            //The out keyword is another new one to know. The out keyword must be used in the method signature for that parameter AND in the calling code.
            //out means that the coder can pass a non-initialized variable and expect to get an initialized variable back.
            //Similarly used as out, the ref keyword means you pass in a reference to the method, rather than a copy of the value. This is helpful when you
            //want the method to be able to modify the parameter and when the method returns, you retain those modifications.
            if (!Int32.TryParse(numberAsString, out parsed))
                System.Diagnostics.Debug.WriteLine("That String can't be parsed, but we didn't get an exception");
            //else, proceeds to use parsed

            return aTest;
        }
    }
}
